﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using Pout_Porri.Objects;
using Pout_Porri.Utils;
using Pout_Porri.Objects.Bosses;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Storage;

namespace Pout_Porri.Levels
{

    /// <summary>
    /// This class can change sets if the player meet the criteria to change level and read de .txt archive from TileSets.
    /// </summary>
    class Level
    {
        /// <summary>
        /// This enum sets the condition to player change level, new condition must be put here
        /// </summary>
        public enum LevelConclusionConditions
        {
            /// <summary>player must kill the boss to progress to the next level</summary>
            KILL_BOSS,
            /// <summary>player must arrive in the end of the level to progress to the next level</summary>
            END,
            /// <summary>player must get a flag to progress to the next level</summary>
            GET_FLAG
        }

        /// <summary>
        /// The get and set condition to finish this level
        /// </summary>
        private LevelConclusionConditions levelConclusionCondition;
        public LevelConclusionConditions LevelConclusionCondition
        {
            get { return levelConclusionCondition; }
           private set { levelConclusionCondition = value; }
        }

        /// <summary>
        /// the number of this level, represents the player's current level 
        /// </summary>
        protected int numLevel;

        /// <summary>
        /// The world of this level
        /// </summary>
        protected World world;

        /// <summary>
        /// The level collisionMatrix
        /// </summary>
        private int[,] collisionMatrix;
        
        /// <summary>
        /// The level collisionMatrix
        /// </summary>
        public int[,] CollisionMatrix
        {
            get { return collisionMatrix; }
            set { collisionMatrix = value; }
        }

        private Texture2D background;
            
        /// <summary>
        /// Level's TileSet.
        /// </summary>
        public Tileset Tileset { get; private set; }

        /// <summary>
        /// the boss of this level
        /// </summary>
        private Boss boss;
        
        /// <summary>
        /// Constructor, receives the number of the level to load
        /// </summary>
        /// <param name="numLevel">A number for this levels</param>
        /// <param name="levelDimensions"> The dimensions of the level</param>
        public Level(int numLevel, Vector2 levelDimensions, ContentManager Content, Texture2D background=null)
        {
            this.numLevel = numLevel;
            this.world = new World(this, levelDimensions);
            this.background = background;
           //load();
            if (this.getNumLevel() == 1)
            {
                load("./Content/Settings/PitfallMap1.tmx");
            }
            else if( this.getNumLevel() == 102)
            {
                load("./Content/Settings/PitfallMap2.tmx");
            }
            else if (this.getNumLevel() == 103)
            {
                load("./Content/Settings/PitfallMap3.tmx");
            }
            else if (this.getNumLevel() == 104)
            {
                load("./Content/Settings/PitfallMap3.tmx");
            }
            else if (this.getNumLevel() == 105)
            {
                load("./Content/Settings/PitfallMap3.tmx");
            }
            else if (this.getNumLevel() == 106)
            {
                load("./Content/Settings/PitfallMap4.tmx");
            }

            else
            {
                load();
            }
           // load2(Content);
        }

        private void load()
        {
            StreamReader sr = new StreamReader("./Content/Settings/level" + numLevel.ToString() + ".txt");

            // get line string
            string line = "";
            // count line
            int countLine = 0;
            int countCol = 0;
            int[,] collisionMatrix = new int[GameConfig.ROWS, GameConfig.COLS];
            while ((line = sr.ReadLine()) != null)
            {
                if (countLine >= GameConfig.ROWS) break;

                string[] lineValues = line.Split(new char[] { ',' });
                for (int j = 0; j < lineValues.Length; j++)
                {
                    if (lineValues[j].Length <= 0) continue;

                    collisionMatrix[countLine, countCol] = int.Parse(lineValues[j]);
                    countCol++;
                    if (countCol >= GameConfig.COLS)
                    {
                        countCol = 0;
                        countLine++;
                    }
                }
            }
            this.collisionMatrix = collisionMatrix;
        }

        

        public void load(string file)
        {
             ReadXMLFile(file);
        }

        private void ReadXMLFile(string file)
        {

            XmlTextReader objXMLTextReader = new XmlTextReader(file);
            string name = "";

            while (objXMLTextReader.Read())
            {
                switch (objXMLTextReader.NodeType)
                {
                    case XmlNodeType.Element:
                        name = objXMLTextReader.Name;
                        break;
                    case XmlNodeType.Text:
                        switch (name)
                        {
                            // Aqui você coloca todas as propriedades que você deseja buscar
                            case "data":
                                ReadMap(objXMLTextReader.Value);
                                break;
                        }
                        break;
                }
            }

        }
        private string removeNewLine(string str)
        {
            return str.Replace("\n", "");
        }

        private void ReadMap(string file)
        {
            file = removeNewLine(file);
            //StreamReader sr = new StreamReader(file);

            // get line string
            string line = "";
            // count line
            int countLine = 0;
            int countCol = 0;
            int[,] collisionMatrix = new int[GameConfig.ROWS, GameConfig.COLS];
            while ((line = file) != null)
            {
                if (countLine >= GameConfig.ROWS) break;

                string[] lineValues = line.Split(new char[] { ',' });
                for (int j = 0; j < lineValues.Length; j++)
                {
                    if (lineValues[j].Length <= 0) continue;

                    collisionMatrix[countLine, countCol] = int.Parse(lineValues[j]);
                    countCol++;
                    if (countCol >= GameConfig.COLS)
                    {
                        countCol = 0;
                        countLine++;
                    }
                }
            }

            this.collisionMatrix = collisionMatrix;
        }

        //NAO ALTERE ESTA FUNCAO -- RISCO DE VIDA
        private void load2(ContentManager Content)
        {
            System.IO.Stream stream = TitleContainer.OpenStream("./Content/Settings/map01.tmx");

            XDocument doc = XDocument.Load(stream);
            List<XElement> properties = doc.Descendants("property").ToList<XElement>();
            //search for the level conclusion condition
            XElement lvlConclusionCondProperty = properties.Find(delegate (XElement obj){
                return obj.Attribute("name").Value == "LevelConclusionCondition";
            });

            levelConclusionCondition = StringToLevelConclusionCondition(lvlConclusionCondProperty.Attribute("value").Value);

            if (levelConclusionCondition == LevelConclusionConditions.KILL_BOSS)
            {
                Texture2D bossImage = null;
                Vector2 bossPosition = Vector2.Zero;
                string bossName = "";
                int bossLife = 0;
                foreach (XElement property in properties)
                {
                    if (property.FirstAttribute.Value == "bossName")
                    {
                        bossName = property.Attribute("value").Value; // get boss name
                    }
                    else if (property.FirstAttribute.Value == "bossImage")
                    {
                        bossImage = Content.Load<Texture2D>(property.Attribute("value").Value);
                    }
                    else if (property.FirstAttribute.Value == "bossPosition")
                    {
                        string[] colRow = property.Attribute("value").Value.Split(new char[] { '|' });
                        int col = int.Parse(colRow[0]) * GameConfig.TILE_SIZE;
                        int row = int.Parse(colRow[1]) * GameConfig.TILE_SIZE;
                        bossPosition.X = col;
                        bossPosition.Y = row;
                    }
                    else if (property.FirstAttribute.Value == "life")
                    {
                        bossLife = int.Parse(property.Attribute("value").Value);
                    }
                }
                this.boss = createBoss(bossName, bossImage, bossPosition,bossLife);
            }

            StreamReader sr = new StreamReader("./Content/Settings/level" + numLevel.ToString() + ".txt");

            // get line string
            string line = "";
            // count line
            int countLine = 0;
            int countCol = 0;
            int[,] collisionMatrix = new int[GameConfig.ROWS, GameConfig.COLS];
            while ((line = sr.ReadLine()) != null)
            {
                if (countLine >= GameConfig.ROWS) break;

                string[] lineValues = line.Split(new char[] { ',' });
                for (int j = 0; j < lineValues.Length; j++)
                {
                    if (lineValues[j].Length <= 0) continue;

                    collisionMatrix[countLine, countCol] = int.Parse(lineValues[j]);
                    countCol++;
                    if (countCol >= GameConfig.COLS)
                    {
                        countCol = 0;
                        countLine++;
                    }
                }
            }

            this.collisionMatrix = collisionMatrix;
        }

        /// <summary>
        /// Convert a string to the correspondant LevelConclusionCondition
        /// </summary>
        /// <param name="str">A level conclusion string</param>
        /// <returns>The level conslusion condition</returns>
        private LevelConclusionConditions StringToLevelConclusionCondition(string str) // NAO ALTERE ESTA FUNCAO -- RISCO DE VIDA
        {
            LevelConclusionConditions condition = LevelConclusionConditions.KILL_BOSS;

            if (str == "KILL_BOSS")
            {
                condition = LevelConclusionConditions.KILL_BOSS;
            }
            else if (str == "END")
            {
                condition = LevelConclusionConditions.END;
            }
            else if (str == "GET_FLAG")
            {
                condition = LevelConclusionConditions.GET_FLAG;
            }

            return condition;
        }

        /// <summary>
        /// Create the boss of this level
        /// </summary>
        /// <param name="image"> an image</param>
        /// <param name="position"> a position</param>
        /// <returns>The boss</returns>
        private Boss createBoss(string name, Texture2D image, Vector2 position, int life)
        {
            Boss boss = new Boss( image, position, Vector2.Zero, life);
            addGameEntity(boss);

            return boss;
        }

        public void setTileset(Tileset tileset)
        {
            this.Tileset = tileset;
        }
        
        /// <summary>
        /// Gets the number from this levels
        /// </summary>
        /// <returns>A number</returns>
        public int getNumLevel()
        {
            return numLevel;
        }
        
        /// <summary>
        /// Sets the number for this level
        /// </summary>
        /// <param name="n">A number</param>
        public void setNumLevel(int n)
        {
            this.numLevel = n;
        }

        public void setPlayer(PhysicsGameEntity player)
        {
            player.MyLevel = this;
            this.world.setWorldActor(player);
        }

        public PhysicsGameEntity getPlayer()
        {
            return this.world.getWorldActor();
        }

        /// <summary>
        /// If the player finished this level
        /// </summary>
        /// <returns>true if the level is finish</returns>
        public bool finishLevel()
        {
            if (levelConclusionCondition == LevelConclusionConditions.KILL_BOSS)
            {
                return boss.IsDead();
            }

            return false;
        }
        
        /// <summary>
        /// Adds a game entity to the list
        /// </summary>
        /// <param name="gE">A game entity</param>
        public void addGameEntity(GameEntity gE)
        {
            if (gE != null)
                world.addGameEntity(gE);
        }
        
        /// <summary>
        /// Removes a game entity from list
        /// </summary>
        /// <param name="gE">A game entity</param>
        public void removeGameEntity(GameEntity gE)
        {
            if (gE != null)
                world.removeGameEntity(gE);
        }

        /// <summary>
        /// Updates all the game entitys on this level
        /// </summary>
        /// <param name="gameTime">The game time</param>
        public void update(GameTime gameTime, KeyboardState keyboardState)
        {
            world.update(gameTime);
        }
        
        /// <summary>
        /// Draw all the entitys on this level
        /// </summary>
        /// <param name="spriteBatch">The spriteBatch</param>
        public void draw(SpriteBatch spriteBatch)
        {
            world.draw(spriteBatch);

            if (background != null)
            {
                spriteBatch.Draw(background, Vector2.Zero, Color.White);
                return;
            }

            Rectangle srcRect = new Rectangle(0, 0, GameConfig.TILE_SIZE, GameConfig.TILE_SIZE);
            Rectangle destRect = new Rectangle(0, 0, GameConfig.TILE_SIZE, GameConfig.TILE_SIZE);

            for (int l = 0; l < collisionMatrix.GetLength(0); l++)
            {
                for (int c = 0; c < collisionMatrix.GetLength(1); c++)
                {
                    if (collisionMatrix[l, c] != 0)
                    {
                        int tile = collisionMatrix[l, c];

                        destRect.X = c * GameConfig.TILE_SIZE;
                        destRect.Y = l * GameConfig.TILE_SIZE;

                        srcRect.X = ((tile - 1) % Tileset.columns) * (GameConfig.TILE_SIZE + Tileset.marginTile) + Tileset.marginTile;
                        srcRect.Y = ((tile - 1) / Tileset.columns) * (GameConfig.TILE_SIZE + Tileset.marginTile) + Tileset.marginTile;

                      
                        spriteBatch.Draw(Tileset.texture, destRect, srcRect, Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, 1f);
                    }
                }
            }
        }
    }
}
